package domain.grammar;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import domain.regularExpression.NonUnaryRegularExpressionOperator;
import domain.regularExpression.RegularExpressionElement;
import domain.regularExpression.UnaryRegularExpressionOperator;

public class NullableCalculator {
	private List<RegularExpressionElement> _nodes;
	private List<Boolean> _flag;
	private List<Boolean> _nullableList;
	private ExtendedGrammar _extGrammar;
	
	public NullableCalculator(ExtendedGrammar extGr){
		_extGrammar = extGr;
		_nodes = new Vector<RegularExpressionElement>();
		Iterator<ExtendedProduction> itProd = extGr.get_productions().get_productions().iterator();
		while(itProd.hasNext()){
			ExtendedProduction extProd = itProd.next();
			_nodes.add(extProd.get_leftSide());
			_nodes.addAll(extProd.get_rightSide().getListOfElemOfClass(ExtendedTerminalSymbol.class));
			_nodes.addAll(extProd.get_rightSide().getListOfElemOfClass(UnaryRegularExpressionOperator.class));
			_nodes.addAll(extProd.get_rightSide().getListOfElemOfClass(NonUnaryRegularExpressionOperator.class));
		}
		_flag = new Vector<Boolean>();
		int index=0;
		while(index<_nodes.size()){
			_flag.add(false);
			index++;
		}
		initializeCalcList();
	}
	
	
	private void initializeCalcList(){
		_nullableList = new Vector<Boolean>();
		int index=0;
		while(index<_nodes.size()){
			_nullableList.add(false);
			index++;
		}
	}
	
	private void uncheckAllNodes(){
		int index = 0;
		while(index<_flag.size()){
			_flag.set(index, false);
			index++;
		}
	}
	
	private void setInNullableList(int nullListIndex, Boolean isNullable){
		boolean oldValue = _nullableList.get(nullListIndex);
		_nullableList.set(nullListIndex, isNullable);
		if(oldValue!=isNullable){
			_flag.set(nullListIndex, true);
		}
	}
	
	private boolean isAllUnchecked(){
		int index = 0;
		boolean result = true;
		while(index<_flag.size() && result){
			result = result && !(_flag.get(index));
			index++;
		}
		return result;
	}
	
	private Map makeMapWithCalculated(){
		Map result = new HashMap<RegularExpressionElement, Set<ExtendedTerminalSymbol>>();
		int index = 0;
		while(index < _nodes.size()){
			result.put(_nodes.get(index), _nullableList.get(index));
			index++;
		}
		return result;
	}
	
	public Map calculateNullables(){
		initializeCalcList();
		makeNullablesIteration();
		while(!isAllUnchecked()){
			makeNullablesIteration();
		}
		return makeMapWithCalculated();
	}
	
	
	private void makeNullablesIteration(){
		uncheckAllNodes();
		int index = 0;
		while(index < _nodes.size()){
			calculateNullableForNode(index);
			index++;
		}
	}
	
	private void calculateNullableForNode(int index) {
		RegularExpressionElement aNode = _nodes.get(index);
		if(aNode instanceof ExtendedTerminalSymbol){
			setInNullableList(index,false);
		}else if(aNode instanceof ExtendedNonTerminalSymbol){
			ExtendedNonTerminalSymbol extNT = (ExtendedNonTerminalSymbol)aNode;
			RegularExpressionElement child = 
				_extGrammar.get_productions().getProductionByLeftSide(extNT).get_rightSide().getRootElement();
			setInNullableList(index,_nullableList.get(_nodes.indexOf(child)));
		}else if(aNode instanceof NonUnaryRegularExpressionOperator){
			NonUnaryRegularExpressionOperator nonUnaryOp = (NonUnaryRegularExpressionOperator) aNode;
			if(nonUnaryOp.isConcatenation()){
				Iterator<RegularExpressionElement> itSubElems = nonUnaryOp.getSubElements().iterator();
				Boolean allChildsNullable = true;
				while(itSubElems.hasNext()){
					RegularExpressionElement aChild = itSubElems.next();
					Boolean isChildNullable = _nullableList.get(_nodes.indexOf(aChild));
					allChildsNullable = allChildsNullable && isChildNullable;
				}
				setInNullableList(index,allChildsNullable);
			}else if(nonUnaryOp.isPipe()){
				Iterator<RegularExpressionElement> itSubElems = nonUnaryOp.getSubElements().iterator();
				Boolean anyChildNullable = false;
				while(itSubElems.hasNext()){
					RegularExpressionElement aChild = itSubElems.next();
					Boolean isChildNullable = _nullableList.get(_nodes.indexOf(aChild));
					anyChildNullable = anyChildNullable || isChildNullable;
				}
				setInNullableList(index,anyChildNullable);
			}
		}else if(aNode instanceof UnaryRegularExpressionOperator){
			UnaryRegularExpressionOperator unOp = (UnaryRegularExpressionOperator)aNode;
			if(unOp.isAsterisk()){
				setInNullableList(index,true);
			}else if(unOp.isInterrogation()){
				setInNullableList(index,true);
			}else if(unOp.isPlus()){
				RegularExpressionElement subElem = unOp.getSubElements().get(0);
				setInNullableList(index,false);
			}
		}
	}
}
